/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

//===========================================================================================
#include <dylab/utils/EntityDefinitionManager.h>
#include <dylab/utils/property/PropertyTree.h>
#include <dylab/utils/log/Log.h>

//===========================================================================================
dylab::EntityDefinitionManager::Definition *
dylab::EntityDefinitionManager::Entity::getDefinition(const String & _defName)
{
	// find the definition
	Definition * def = mDefinitionMap.find(_defName); 
	if (def == NULL)
		throw EDoesNotExists(String::fmt("The requested definition does not exists: \"%1%\"", _defName));

	// and return it
	return def;
}
//-----------------------------------------------------------------------------
const dylab::EntityDefinitionManager::Definition *
dylab::EntityDefinitionManager::Entity::getDefinition(const String & _defName) const
{
	// find the definition
	Definition * def = mDefinitionMap.find(_defName); 
	if (def == NULL)
		throw EDoesNotExists(String::fmt("The requested definition does not exists: \"%1%\"", _defName));

	// and return it
	return def;
}
//-----------------------------------------------------------------------------

//===========================================================================================
dylab::EntityDefinitionManager::EntityDefinitionManager()
{ 
}
//-----------------------------------------------------------------------------
dylab::EntityDefinitionManager::~EntityDefinitionManager()
{ 
}
//-----------------------------------------------------------------------------
dylab::EntityDefinitionManager::Entity * 
dylab::EntityDefinitionManager::addEntity(const String & _entityName)
{ 
	// create new entity
	Entity * entity = new Entity(this, _entityName);
	DYLAB_ASSERT(entity != NULL);

	// add it to the entity map
	mEntityMap.insert(_entityName, entity); 

	// return the newly created entity
	return entity;
}
//-----------------------------------------------------------------------------
dylab::EntityDefinitionManager::Entity *
dylab::EntityDefinitionManager::getEntity(const String & _entityName)
{
	// find the entity
	Entity * entity = mEntityMap.find(_entityName); 
	if (entity == NULL)
		throw EDoesNotExists(String::fmt("The requested entity does not exists: \"%1%\"", _entityName));

	// and return it
	return entity;
}
//-----------------------------------------------------------------------------
void dylab::EntityDefinitionManager::loadDefinitionFile(const String & _fileName)
{
	DYLAB_TRACEF("Loading definition file: %1%", _fileName);

    // load the definition file        
    PropertyTree defPT;
	defPT.load(mBasePath + _fileName);	

    // for each definition group
	for (PropertyTree::NodeListMap::Iterator defList(defPT.getNodeListMap()); defList.isValid(); defList++)                            
    {
		// retrieve the group
		Entity * grp = mEntityMap.get(~defList);

		DYLAB_TRACEF("Adding definitions for entity: \"%1%\"", grp->getName());

		// for each definition in this group
		for (PropertyTree::NodeList::Iterator def(*defList); def.isValid(); def++)
		{
			// create new definition with appropriate name and property tree
			Definition * entDef = new Definition(grp, def->getValue(), def->getChildren());

			// recursivelly expand the property tree
			expandPropertyTree(grp->getName(), entDef->getPropertyTree());

			// insert new created definition
			grp->getDefinitionMap().insert(entDef->getName(), entDef);

			DYLAB_TRACEF2("Added definition \"%1%\": %2%", entDef->getName(), entDef->getPropertyTree());
		}
    }

	DYLAB_TRACEF("Definition file loaded: %1%", _fileName);
}
//-----------------------------------------------------------------------------
void dylab::EntityDefinitionManager::expandPropertyTree(const String & _entityName, PropertyTree & _pt)
{
	// if there is an attribute called "@base"
	PropertyTree::Node * baseNode = _pt.findNode("@base");
	if (baseNode != NULL)
	{
		DYLAB_TRACEF2("Expanding from base definition \"%2%\" from entity \"%1%\"", _entityName, baseNode->getValue());

		// copy in the base property tree
		getEntity(_entityName)->getDefinition(baseNode->getValue())->getPropertyTree().copy(_pt);

		// delete the "@base" attribute
		_pt.destroyProperty("@base");
	}

	// repeat the expand process for all of its child trees
	for (PropertyTree::NodeListMap::Iterator nodeList(_pt.getNodeListMap()); nodeList.isValid(); nodeList++)
		for (PropertyTree::NodeList::Iterator node(*nodeList); node.isValid(); node++)
			expandPropertyTree(node->getName(), node->getChildren());
}
//-----------------------------------------------------------------------------
void dylab::EntityDefinitionManager::loadFromConfigFile(const String & _fileName)
{
    // load the file into the property tree
	PropertyTree edf;
    edf.load(_fileName);

    // set base directory for the module look-up
    setBasePath(edf.getNodeValue("basePath"));

    // register entities based on the info from the loaded configuration file	
	if (edf.getNodeCount("entity") > 0)
	{
		for (PropertyTree::NodeList::Iterator entity(*edf.getNodeList("entity")); entity.isValid(); entity++)                            
		{
			addEntity(entity->getValue());
			DYLAB_TRACEF("Added entity: \"%1%\"", entity->getValue());
		}
	}

    // load all definition files
    for (PropertyTree::NodeList::Iterator file(*edf.getNodeList("file")); file.isValid(); file++)                            
	{
		loadDefinitionFile(file->getValue());
		DYLAB_TRACEF("Added file: \"%1%\"", file->getValue());
	}
}
//-----------------------------------------------------------------------------